package com.lianqi.emcpframework.ddd.gencode.plugin;

import java.lang.annotation.Annotation;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.lang.model.element.AnnotationMirror;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import javax.validation.Valid;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.validator.constraints.*;

import com.lianqi.emcpframework.ddd.gencode.EnableGenForAggregate;
import com.lianqi.emcpframework.ddd.gencode.EnableGenForEntity;
import com.lianqi.emcpframework.ddd.gencode.define.JavaSource;
import com.lianqi.emcpframework.ddd.gencode.updater.GenUpdater;
import com.lianqi.emcpframework.ddd.gencode.updater.GenUpdaterIgnore;
import com.lianqi.emcpframework.ddd.gencode.updater.GenUpdaterParser;
import com.lianqi.emcpframework.ddd.gencode.updater.support.meta.UpdaterMeta;
import com.lianqi.emcpframework.ddd.gencode.updater.support.parser.UpdaterMetaParser;
import com.lianqi.emcpframework.ddd.gencode.updater.support.writer.UpdaterMethodWriter;
import com.squareup.javapoet.*;

import lombok.Data;

public class GenUpdaterPlugin extends AbstractProcessorPlugin {
    private final UpdaterMetaParser metaParser = new UpdaterMetaParser();
    private final UpdaterMethodWriter creatorMethodWriter = new UpdaterMethodWriter();

    @Override
    protected void process(TypeElement typeElement, Annotation annotation) {
        GenUpdaterParser parser = new GenUpdaterParser(typeElement);
        parser.read(annotation);
        String packageName = parser.getPackageName();
        String className = parser.getClassName();

        String parentClassName = parser.getParentClassName();

        TypeVariableName typeVariableName = TypeVariableName.get("T extends " + className);
        /**
         * 添加判断 是否直接使用生成的updater
         */
        boolean useDirect = false;
        if (annotation instanceof GenUpdater) {
            useDirect = ((GenUpdater)annotation).useDirect();
        } else if (annotation instanceof EnableGenForAggregate) {
            useDirect = ((EnableGenForAggregate)annotation).useUpdaterDirect();
        } else if (annotation instanceof EnableGenForEntity) {
            useDirect = ((EnableGenForEntity)annotation).useUpdaterDirect();
        }
        TypeSpec.Builder typeSpecBuilder = null;
        if (useDirect) {
            /**
             * 生成非抽象类
             */
            typeSpecBuilder = TypeSpec.classBuilder(className).addTypeVariable(typeVariableName)
                .addAnnotation(Data.class).addModifiers(Modifier.PUBLIC);
        } else {
            /**
             * 生成抽象类
             */
            typeSpecBuilder = TypeSpec.classBuilder(className).addTypeVariable(typeVariableName)
                .addAnnotation(Data.class).addModifiers(Modifier.PUBLIC, Modifier.ABSTRACT);
        }

        if (StringUtils.isNotEmpty(parentClassName)) {
            ClassName parent = ClassName.bestGuess(parentClassName);
            TypeName typeName = TypeVariableName.get("T");
            ParameterizedTypeName parameterizedTypeName = ParameterizedTypeName.get(parent, typeName);
            typeSpecBuilder.superclass(parameterizedTypeName);
        }

        MethodSpec.Builder acceptMethodBuilder =
            MethodSpec.methodBuilder("accept").addModifiers(Modifier.PUBLIC).returns(TypeName.VOID)
                .addParameter(ParameterSpec.builder(TypeName.get(typeElement.asType()), "target").build());
        if (StringUtils.isNotEmpty(parentClassName)) {
            acceptMethodBuilder.addStatement("super.accept(target)");
        }

        UpdaterMeta creatorMeta = this.metaParser.parse(typeElement);
        // this.creatorMethodWriter.writeTo(typeSpecBuilder, acceptMethodBuilder, creatorMeta.getMethodMetas());
        this.creatorMethodWriter.writeTo(typeSpecBuilder, acceptMethodBuilder, creatorMeta.getMethodMetas(),
            findValidAnnotation(typeElement));
        typeSpecBuilder.addMethod(acceptMethodBuilder.build());

        getJavaSourceCollector().register(new JavaSource(packageName, className, typeSpecBuilder));
    }

    /**
     * 查找字段上带有注解相关的内容
     * 
     * @param typeElement
     *            实体元素
     * @return
     */
    private Map<String, AnnotationMirror> findValidAnnotation(TypeElement typeElement) {
        Map<String, AnnotationMirror> annotationMap = new HashMap<>();
        List<VariableElement> fileds = ElementFilter.fieldsIn(typeElement.getEnclosedElements());
        for (VariableElement element : fileds) {
            if (!element.getModifiers().contains(Modifier.STATIC)) {
                putIfAbsent(element, annotationMap);
            }
        }
        return annotationMap;
    }

    /**
     * 支持的校验注解
     */
    private static final Class[] ANNOTATION_ARRAY = {Valid.class, Length.class, Email.class, Range.class,
        ScriptAssert.class, URL.class, CreditCardNumber.class, ModCheck.class};

    /**
     * 找到字段匹配的注解 并放入map映射存储
     * 
     * @param element
     *            字段元素
     * @param annotationMap
     *            key：字段名 value：注解镜像
     */
    private void putIfAbsent(VariableElement element, Map<String, AnnotationMirror> annotationMap) {

        List<AnnotationMirror> mirrors = (List<AnnotationMirror>)element.getAnnotationMirrors();
        for (AnnotationMirror mirror : mirrors) {
            for (Class clz : ANNOTATION_ARRAY) {
                if (mirror.getAnnotationType().asElement().asType().toString().equals(clz.getName())) {
                    annotationMap.put(element.getSimpleName().toString(), mirror);
                }
            }
        }
    }

    @Override
    public Class<Annotation>[] applyAnnCls() {
        return new Class[] {GenUpdater.class, EnableGenForAggregate.class, EnableGenForEntity.class};
    }

    @Override
    public <A extends Annotation> Class<A>[] ignoreAnnCls() {
        return new Class[] {GenUpdaterIgnore.class};
    }
}
